#ifndef WINDOW_CONTROL_H
#define WINDOW_CONTROL_H
#include "precompiledHeades.h"

class Window
{
public:
    Window(): _hInst(NULL), _hParent(NULL), _hSelf(NULL) { /* NOP */ }
    virtual ~Window(){ /* NOP */ }

    virtual void init(HINSTANCE hInst, HWND parent) {
        _hInst = hInst;
        _hParent = parent;
    }

    virtual void destroy() = 0;

    virtual void display(bool toShow = true) const {
        ::ShowWindow(_hSelf, toShow ? SW_SHOW : SW_HIDE);
    }

    virtual void resizeTo(RECT& rc) // Should NEVER be const
    {
        /*
         Change the position and dimensions of the specified window. For a top-level window,
         the position and dimensions are relative to the upper-left corner of the screen.
         For a child window, they are relative to the upper-left corner of the parent
         window 's client area.

         BOOL MoveWindow(HWND hWnd, int X, int Y, int nWidth, int nHeight, BOOl bRepaint);

         bRepaint: Indicates whether the window is to be repainted.
                   If TRUE, the window receives a message.
                   If FALSE, no repainting of any kind occurs. This applies to the client area,
                   the nonclient area (including title bar and scroll bars) and any part
                   of the parent window uncovered as a result of moving a child window.

         If bRepaint is TRUE, the system sends WM_PAINT message to the window procedure
         immediately after moving the window (that is, the MoveWindow calls UpdateWindow).
         If bRepaint is FALSE, the applicatiom must explicitly invalidate or redraw any parts
         of the window and parent that need redrawing.
         MoveWindow sends the WM_WINDOWPOSCHANGING, WM_WINDOWPOSCHANGED, WM_MOVE, WM_SIZE,
         and WM_NCCALCSIZE messages to the window.
         */
        ::MoveWindow(_hSelf, rc.left, rc.top, rc.right, rc.bottom, TRUE);
    }

    virtual void resizeToWH(RECT& rc) // Should NEVER be const
    {
        ::MoveWindow(_hSelf, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
    }

    virtual void redraw(bool forceUpdate = false) const {
        /*
         InvalidateRect adds a rectangle to specified window's update region.
         The update region represents the portion of the window's client area
         that must be redrawn.
         InvalidateRect(HWND hWnd, const RECT* lpRect, BOOL bErase);
         hWnd: if NULL, system invalidates and redraws all windows,
               not just the windows for this application, and
               sends WM_ERASEBKGND and WM_NCPAINT message before
               the function returns.
         lpRect: the client coordinates of the rectangle to be added to the update region.
                 If NULL, the entire client area is added to the update region.
         bErase: if TRUE, background is erased in the entire region,
                 not just in the specified part when the BeginPaint is called.
                 If FALSE, the background remains unchanged.
         The invalidated areas accumulate in the update region until the region is processed
         when the next WM_PAINT message occurs or until the region is validated by using
         ValidateRect or ValidateRgn function.
         The systems sends a WM_PAINT whenever its update region is not empty and there are
         no other messages in the application queue for that window.
         */
        ::InvalidateRect(_hSelf, NULL, TRUE);
        if (forceUpdate) {
            /*
             UpdateWindow updates the client area of the specified window by sending a WM_PAINT
             to the window if the window's update region is not empty. The function sends
             a WM_PAINT message directly to the window procedure of the specified window,
             bypassing the application queue. If the update region is empty, no message is sent.
             */
            ::UpdateWindow(_hSelf);
        }
    }

    virtual void getClientRect(RECT& rc) const {
        ::GetClientRect(_hSelf, &rc);
    }

    virtual void getWindowRect(RECT& rc) const {
        ::GetWindowRect(_hSelf, &rc);
    }

    virtual int getWidth() const {
        RECT rc;
        ::GetClientRect(_hSelf, &rc);
        return rc.right - rc.left;
    }

    virtual int getHeight() const {
        RECT rc;
        ::GetClientRect(_hSelf, &rc);
        if (::IsWindowVisible(_hSelf) == TRUE)
            return rc.bottom - rc.top;
        return 0;
    }

    virtual bool isVisible() const {
        return ::IsWindowVisible(_hSelf) ? true : false;
    }

    HWND getHSelf() const {
        assert(_hSelf != NULL);
        return _hSelf;
    }

    HWND getHParent() const {
        return _hParent;
    }

    HINSTANCE getHinst() const {
        assert(_hInst != nullptr);
        return _hInst;
    }

    void getFocus() const {
        /*
         SetFocus sends a WM_KILLFOCUS message to the window
         that loses the keyboard focus and a WM_SETFOCUS message
         to the window that receives the keyboard focus. It also
         activates either the window that receives the focus
         or the parent of the window that receives the focus.

         By using the AttachThreadInput function, a thread can
         attach its input processing to another thread. This allows
         a thread to call SetFocus to set the keyboard focus to a
         window attached to another thread' message queue.
         */
        ::SetFocus(_hSelf);
    }

protected:
   HINSTANCE _hInst;
   HWND _hParent;
   HWND _hSelf;
};

#endif // WINDOW_CONTROL_H
